package com.hello.sandbox.core.system.user;

import android.os.Parcel;
import android.os.RemoteException;
import androidx.core.util.AtomicFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import com.hello.sandbox.core.env.BEnvironment;
import com.hello.sandbox.core.system.ISystemService;
import com.hello.sandbox.core.system.pm.BPackageManagerService;
import com.hello.sandbox.utils.CloseUtils;
import com.hello.sandbox.utils.FileUtils;

/** Created by Milk on 4/22/21. * ∧＿∧ (`･ω･∥ 丶　つ０ しーＪ 此处无Bug */
public class BUserManagerService extends IBUserManagerService.Stub implements ISystemService {
  private static BUserManagerService sService = new BUserManagerService();
  public final HashMap<Integer, BUserInfo> mUsers = new HashMap<>();
  public final Object mUserLock = new Object();

  public static BUserManagerService get() {
    return sService;
  }

  @Override
  public void systemReady() {
    scanUserL();
  }

  @Override
  public BUserInfo getUserInfo(int userId) {
    synchronized (mUserLock) {
      return mUsers.get(userId);
    }
  }

  @Override
  public boolean exists(int userId) {
    synchronized (mUsers) {
      return mUsers.get(userId) != null;
    }
  }

  @Override
  public BUserInfo createUser(int userId) throws RemoteException {
    synchronized (mUserLock) {
      if (exists(userId)) {
        return getUserInfo(userId);
      }
      return createUserLocked(userId);
    }
  }

  @Override
  public List<BUserInfo> getUsers() {
    synchronized (mUsers) {
      ArrayList<BUserInfo> bUsers = new ArrayList<>();
      for (BUserInfo value : mUsers.values()) {
        if (value.id >= 0) {
          bUsers.add(value);
        }
      }
      return bUsers;
    }
  }

  public List<BUserInfo> getAllUsers() {
    synchronized (mUsers) {
      return new ArrayList<>(mUsers.values());
    }
  }

  @Override
  public void deleteUser(int userId) throws RemoteException {
    synchronized (mUserLock) {
      synchronized (mUsers) {
        BPackageManagerService.get().deleteUser(userId);

        mUsers.remove(userId);
        saveUserInfoLocked();
        FileUtils.deleteDir(BEnvironment.getUserDir(userId));
        FileUtils.deleteDir(BEnvironment.getExternalUserDir(userId));
      }
    }
  }

  private BUserInfo createUserLocked(int userId) {
    BUserInfo bUserInfo = new BUserInfo();
    bUserInfo.id = userId;
    bUserInfo.status = BUserStatus.ENABLE;
    mUsers.put(userId, bUserInfo);
    synchronized (mUsers) {
      saveUserInfoLocked();
    }
    return bUserInfo;
  }

  private void saveUserInfoLocked() {
    Parcel parcel = Parcel.obtain();
    AtomicFile atomicFile = new AtomicFile(BEnvironment.getUserInfoConf());
    FileOutputStream fileOutputStream = null;
    try {
      ArrayList<BUserInfo> bUsers = new ArrayList<>(mUsers.values());
      parcel.writeTypedList(bUsers);
      try {
        fileOutputStream = atomicFile.startWrite();
        FileUtils.writeParcelToOutput(parcel, fileOutputStream);
        atomicFile.finishWrite(fileOutputStream);
      } catch (IOException e) {
        e.printStackTrace();
        atomicFile.failWrite(fileOutputStream);
      } finally {
        CloseUtils.close(fileOutputStream);
      }
    } finally {
      parcel.recycle();
    }
  }

  private void scanUserL() {
    synchronized (mUserLock) {
      Parcel parcel = Parcel.obtain();
      InputStream is = null;
      try {
        File userInfoConf = BEnvironment.getUserInfoConf();
        if (!userInfoConf.exists()) {
          return;
        }
        is = new FileInputStream(BEnvironment.getUserInfoConf());
        byte[] bytes = FileUtils.toByteArray(is);
        parcel.unmarshall(bytes, 0, bytes.length);
        parcel.setDataPosition(0);

        ArrayList<BUserInfo> loadUsers = parcel.createTypedArrayList(BUserInfo.CREATOR);
        if (loadUsers == null) return;
        synchronized (mUsers) {
          mUsers.clear();
          for (BUserInfo loadUser : loadUsers) {
            mUsers.put(loadUser.id, loadUser);
          }
        }
      } catch (Exception e) {
        e.printStackTrace();
      } finally {
        parcel.recycle();
        CloseUtils.close(is);
      }
    }
  }
}
